﻿/*
 * Divisionsmatch - beregning af resultater
 * Copyright (C) 2013 Anders Klinting
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Print = System.Drawing.Printing;

namespace Divisionsmatch
{
    class clsPrinterService
    {
        # region Class interface with project

        public clsPrinterService()
        {
            Print.PageSettings objPrinting = new Print.PageSettings();
            psctUserPrinterOptions = new USER_PRINTER_OPTIONS();
            psctUserPrinterOptions.pstrPrinterName = objPrinting.PrinterSettings.PrinterName;
            Boolean blnNOTUSER = PrinterSettings;
        }

        private USER_PRINTER_OPTIONS psctUserPrinterOptions;

        private struct USER_PRINTER_OPTIONS
        {
            public string pstrPrinterName;
            public short pshtDuplex;
        }

        public short DM_DUPLEX_OFF
        {
            get
            {
                return 1;
            }
        }

        public short DM_DUPLEX_SHORT
        {
            get
            {
                return 2;
            }
        }

        public short DM_DUPLEX_LONG
        {
            get
            {
                return 3;
            }
        }

        public string strPrinterName
        {
            get
            {
                return psctUserPrinterOptions.pstrPrinterName;
            }

            set
            {
                psctUserPrinterOptions.pstrPrinterName = value;
            }
        }

        public short shtDuplex
        {
            get
            {
                return psctUserPrinterOptions.pshtDuplex;
            }

            set
            {
                psctUserPrinterOptions.pshtDuplex = value;
                PrinterSettings = true;
            }
        }

        #endregion

        #region API Code to interface with Kernel32 and Winspool (From Microsoft)

        #region API Interfaces

        //From Microsoft

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, string[] Arguments);

        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetDefaultPrinter(string pszPrinter);

        [DllImport("winspool.drv", EntryPoint = "DocumentPropertiesA", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, string pDeviceNameg, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);

        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, ref PRINTER_DEFAULTS pDefaults);

        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, uint Command);

        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetPrinter(IntPtr hPrinter, int dwLevel,

        IntPtr pPrinter, int cbBuf, out int pcbNeeded);

        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool ClosePrinter(IntPtr hPrinter);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

        #endregion

        #region API Structs

        private struct PRINTER_DEFAULTS
        {
            public string DataType;
            public IntPtr DevMode;
            public uint DesiredAccess;
        }

        [StructLayout(LayoutKind.Sequential)]

        private struct PRINTER_INFO_9
        {
            public IntPtr DevMode;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

        private struct DEVMODE
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]

            public string dmDeviceName;
            public short dmSpecVersion;
            public short dmDriverVersion;
            public short dmSize;
            public short dmDriverExtra;
            public int dmFields;
            public short dmOrientation;
            public short dmPaperSize;
            public short dmPaperLength;
            public short dmPaperWidth;
            public short dmScale;
            public short dmCopies;
            public short dmDefaultSource;
            public short dmPrintQuality;
            public short dmColor;
            public short dmDuplex;
            public short dmYResolution;
            public short dmTTOption;
            public short dmCollate;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
            public string dmFormName;
            public short dmUnusedPadding;
            public int dmBitsPerPel;
            public int dmPelsWidth;
            public int dmPelsHeight;
            public int dmDisplayFlags;
            public int dmDisplayFrequency;
        }

        #endregion

        #region API Constants

        private const int DM_OUT_BUFFER = 2;
        private const int PRINTER_ACCESS_ADMINISTER = 0x4;
        private const int PRINTER_ACCESS_USE = 0x8;
        private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        private const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
        private const short CCDEVICENAME = 32;
        private const short CCFORMNAME = 32;

        #endregion

        #region API Methods

        private Boolean PrinterSettings
        {
            get
            {
                //  See the set method for the interpretation of this code
                string strPrinterName = psctUserPrinterOptions.pstrPrinterName;

                PRINTER_DEFAULTS PrinterDefaults = new PRINTER_DEFAULTS();
                PrinterDefaults.DataType = null;
                PrinterDefaults.DevMode = IntPtr.Zero;
                PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;
                IntPtr PrinterPointer;
                int intBytesNeededForPrinter = 0;
                Boolean blnResult = OpenPrinter(strPrinterName, out PrinterPointer, ref PrinterDefaults);
                GetPrinter(PrinterPointer, 9, IntPtr.Zero, 0, out intBytesNeededForPrinter);
                IntPtr ptrPrinterInfo = Marshal.AllocCoTaskMem(intBytesNeededForPrinter);
                GetPrinter(PrinterPointer, 9, ptrPrinterInfo, intBytesNeededForPrinter, out intBytesNeededForPrinter);
                PRINTER_INFO_9 PrinterInfo_9 = (PRINTER_INFO_9)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_9));
                DEVMODE PrinterDevMode_9 = (DEVMODE)Marshal.PtrToStructure(PrinterInfo_9.DevMode, typeof(DEVMODE));
                psctUserPrinterOptions.pshtDuplex = PrinterDevMode_9.dmDuplex;

                ClosePrinter(PrinterPointer);

                return true;
            }

            set
            {

                string strPrinterName = psctUserPrinterOptions.pstrPrinterName;

                PRINTER_DEFAULTS PrinterDefaults = new PRINTER_DEFAULTS();

                PrinterDefaults.DataType = null;

                PrinterDefaults.DevMode = IntPtr.Zero;

                PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;

                IntPtr PrinterPointer;

                int intBytesNeededForPrinter = 0;

                //  First we use the strPrinterName and PrinterDefaults to return the PrinterPointer from the winspool.drv

                Boolean blnResult = OpenPrinter(strPrinterName, out PrinterPointer, ref PrinterDefaults);

                //  Using the PrinterPointer we can get the intBytesNeededForPrinter we don’t know the 3rd & 4th parmenters yet
                //  TODO: Not sure what the 9 (dwLevel) is for yet ???
                //  Now we know the intBytesNeededForPrinter ptrPrinterInfo Marshal.AllocCoTaskMem sets up a slot for the ptrPrinterInfo
                //  Using the PrinterPointer we can add data to the ptrPrinterInfo
                //  Using the ptrPrinterInfo we can convert this into a struct of the PRINTER_INFO_9 type
                GetPrinter(PrinterPointer, 9, IntPtr.Zero, 0, out intBytesNeededForPrinter);

                IntPtr ptrPrinterInfo = Marshal.AllocCoTaskMem(intBytesNeededForPrinter);

                GetPrinter(PrinterPointer, 9, ptrPrinterInfo, intBytesNeededForPrinter, out intBytesNeededForPrinter);
                PRINTER_INFO_9 PrinterInfo_9 = (PRINTER_INFO_9)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_9));
                DEVMODE PrinterDevMode_9 = (DEVMODE)Marshal.PtrToStructure(PrinterInfo_9.DevMode, typeof(DEVMODE));

                //  This is the simple change of one setting
                PrinterDevMode_9.dmDuplex = psctUserPrinterOptions.pshtDuplex;

                //  Revearse the struct back into bytyes and then set the printer with the new value
                Marshal.StructureToPtr(PrinterDevMode_9, PrinterInfo_9.DevMode, true);
                Marshal.StructureToPtr(PrinterInfo_9, ptrPrinterInfo, true);
                blnResult = SetPrinter(PrinterPointer, 9, ptrPrinterInfo, 0);

                if (blnResult == false)
                {
                    MessageBox.Show("Error in setting printer");
                }

                ClosePrinter(PrinterPointer);
                Boolean blnTemp = value;
            }

        #endregion
        }

        #endregion
    }
}
